/*      SClassArray.cpp
 *
 *      Copyright 2009-2010 virpool <virpool@ukrpost.ua>
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

#include "SClassArray.h"
#include "SClassInteger.h"
#include "SClassFloat.h"
#include "SClassString.h"
#include "SClassBoolean.h"

SClassArray::SClassArray(std::string name, bool isdynamic, S_ARG_TYPES type,
		unsigned long size) :
	SObject(name, "Array")
{
	// TODO Auto-generated constructor stub
	this->isdynamic = isdynamic;
	this->size = size;
	this->cur_size = 0;
	switch (type)
	{
	case S_OBJECT:
		this->ar_type = "Object";
		break;
	case S_INT:
		this->ar_type = "Integer";
		break;
	case S_FLOAT:
		this->ar_type = "Float";
		break;
	case S_STRING:
		this->ar_type = "String";
		break;
	case S_BOOL:
		this->ar_type = "Boolean";
		break;
	case S_ARRAY:
		break;
	case S_HASH:
		break;
	case S_TYPE:
		break;
	}
	if (!isdynamic)
	{
		if (size > 0 && size < 10000)
		{
			s_value = new SObject*[size];
		}
		else
		{
			//throw exception("incorrect bounds of array!");
		}
	}
	//GetMethods()->erase("ToString");
	//GetMethods()->insert(pair<std::string, s_func>("ToString", &SClassArray::ToString));
	GetMethods()->insert(pair<std::string, s_func> ("Length", &SClassArray::Length));
	GetMethods()->insert(pair<std::string, s_func> ("IsDynamic",
			&SClassArray::IsDymanic));
	GetMethods()->insert(pair<std::string, s_func> ("IsStatic",
			&SClassArray::IsStatic));
	GetMethods()->insert(pair<std::string, s_func> ("Add", &SClassArray::Add));
	GetMethods()->insert(pair<std::string, s_func> ("Invert", &SClassArray::Invert));
	GetMethods()->insert(pair<std::string, s_func> ("Sort", &SClassArray::Sort));
	GetMethods()->insert(pair<std::string, s_func> ("Clear", &SClassArray::Clear));
	GetMethods()->insert(pair<std::string, s_func> ("Join", &SClassArray::Join));
}

SClassArray::~SClassArray()
{
	// TODO Auto-generated destructor stub
	if (!isdynamic)
	{
		for (register unsigned long i = 0; i < size; i++)
		{
			delete s_value[i];
			delete[] s_value;
		}
	}
}

SObject* SClassArray::Get(unsigned long ind)
{
	if ((!isdynamic && ind > size) || (isdynamic && ind > d_value.size()))
		return NULL;
	if (isdynamic)
	{
		list<SObject*>::iterator p = d_value.begin();
		for (register unsigned long i = 0; i < ind; i++, p++)
			;
		return *p;
	}
	else
	{
		return s_value[ind];
	}
}

void SClassArray::Set(unsigned long ind, SObject *obj)
{
	if (ind > size || (isdynamic && ind > d_value.size()))
		return;
	if (isdynamic)
	{
		list<SObject*>::iterator p = d_value.begin();
		for (register unsigned long i = 0; i < ind; i++, p++)
			;
		*p = obj;
	}
	else
	{
		s_value[ind] = obj;
	}
}

int SClassArray::ToString(SObject* obj, s_param* res, param_list* args)
{
	return S_INVOKE_OK;
}

int SClassArray::Length(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL || res == NULL)
	{
		return S_INVOKE_INCORRECT;
	}
	//cerr << "Cur_length = " << itoa(((SClassArray*) obj)->cur_size, 10) << endl;
	res->type = S_STRING;
	static std::string r = itoa(((SClassArray*) obj)->cur_size, 10);
	res->val = &r;
	return S_INVOKE_OK;
}

int SClassArray::IsDymanic(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL || res == NULL)
	{
		return S_INVOKE_INCORRECT;
	}
	static std::string r;
	res->type = S_STRING;
	if (((SClassArray*) obj)->isdynamic)
	{
		r = "true";
	}
	else
	{
		r = "false";
	}
	res->val = &r;
	return S_INVOKE_OK;
}

int SClassArray::IsStatic(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL || res == NULL)
	{
		return S_INVOKE_INCORRECT;
	}
	static std::string r;
	res->type = S_STRING;
	if (!((SClassArray*) obj)->isdynamic)
	{
		r = "true";
	}
	else
	{
		r = "false";
	}
	res->val = &r;
	return S_INVOKE_OK;
}

int SClassArray::Add(SObject* obj, s_param* res, param_list* args)
{
	//cerr << "In method Array.Add\n";
	if (obj == NULL || args == NULL)
	{
		return S_INVOKE_INCORRECT;
	}
	param_list::iterator p = args->begin();
	while (p != args->end())
	{
		SObject *nel = NULL;
		switch ((*p).type)
		{
		case S_OBJECT:
		{
			std::string cn = ((SObject*) p->val)->GetClassName();
			if (cn != ((SClassArray*) obj)->ar_type)
				return S_INVOKE_INCORRECT;
			
			if (cn == "Integer")
			{
				nel = new SClassInteger("none");
				((SClassInteger*) nel)->Set(((SClassInteger*) p->val)->Get());
				break;
			}
			else if (cn == "Float")
			{
				nel = new SClassFloat("none");
				((SClassFloat*) nel)->Set(((SClassFloat*) p->val)->Get());
				break;
			}
			else if (cn == "String")
			{
				nel = new SClassString("none");
				((SClassString*) nel)->Set(((SClassString*) p->val)->Get());
				break;
			}
			else if (cn == "Boolean")
			{
				nel = new SClassBoolean("none");
				((SClassBoolean*) nel)->Set(((SClassBoolean*) p->val)->Get());
				break;
			}
			else
				return S_INVOKE_INCORRECT;
			break;
		}
		case S_INT:
		{
			nel = new SClassInteger("none");
			((SClassInteger*) nel)->Set((*(int*) (*p).val));
			break;
		}
		case S_FLOAT:
		{
			nel = new SClassFloat("none");
			((SClassFloat*) nel)->Set((*(double*) (*p).val));
			break;
		}
		case S_STRING:
		{
			nel = new SClassString("none");
			((SClassString*) nel)->Set((*(std::string*) (*p).val));
			break;
		}
		case S_BOOL:
		{
			nel = new SClassBoolean("none");
			((SClassBoolean*) nel)->Set((*(bool*) (*p).val));
			break;
		}
		default:
			return S_INVOKE_INCORRECT;
		}
		//cerr << "nel classname = " << nel->GetClassName() << endl;
		//cerr << "array type = " << ((SClassArray*) obj)->ar_type << endl;
		if (((SClassArray*) obj)->ar_type != "General"
				&& ((SClassArray*) obj)->ar_type != nel->GetClassName())
		{
			delete nel;
			return S_INVOKE_INCORRECT;
		}
		if (((SClassArray*) obj)->isdynamic)
		{
			//cerr << "kakbe add in dynamic\n";
			((SClassArray*) obj)->d_value.push_back(nel);
		}
		else
		{
			((SClassArray*) obj)->s_value[((SClassArray*) obj)->cur_size] = nel;
		}
		((SClassArray*) obj)->cur_size++;
		p++;
	}
	return S_INVOKE_OK;
}

int SClassArray::Invert(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL)
		return S_INVOKE_INCORRECT;
	if (((SClassArray*) obj)->isdynamic)
	{
		((SClassArray*) obj)->d_value.reverse();
	}
	else
	{
		int size = ((SClassArray*) obj)->cur_size;
		for (register unsigned long i = 0; i < (int) size / 2; i++)
		{
			SObject *tmp = ((SClassArray*) obj)->s_value[i];
			((SClassArray*) obj)->s_value[i]
					= ((SClassArray*) obj)->s_value[size - i - 1];
			((SClassArray*) obj)->s_value[size - i - 1] = tmp;
		}
	}
	return S_INVOKE_OK;
}

int SClassArray::Sort(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL)
		return S_INVOKE_INCORRECT;
	if (((SClassArray*) obj)->isdynamic)
	{
		((SClassArray*) obj)->d_value.sort();
	}
	else
	{
		if (((SClassArray*) obj)->ar_type == "String")
		{
			std::string *tmp = new std::string[((SClassArray*) obj)->cur_size];
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
				tmp[i]
						= ((SClassString*) ((SClassArray*) obj)->s_value[i])->Get();
			qsort(tmp, ((SClassArray*) obj)->cur_size, sizeof(int), cmpstring);
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
				((SClassString*) ((SClassArray*) obj)->s_value[i])->Set(tmp[i]);
			delete []tmp;
		}
		else if (((SClassArray*) obj)->ar_type == "Integer")
		{
			int *tmp = new int[((SClassArray*) obj)->cur_size];
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
				tmp[i]
						= ((SClassInteger*) ((SClassArray*) obj)->s_value[i])->Get();
			qsort(tmp, ((SClassArray*) obj)->cur_size, sizeof(int), cmpint);
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
				((SClassInteger*) ((SClassArray*) obj)->s_value[i])->Set(tmp[i]);
			delete []tmp;
		} else if (((SClassArray*) obj)->ar_type == "Float")
		{
			float *tmp = new float[((SClassArray*) obj)->cur_size];
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
				tmp[i]
						= ((SClassFloat*) ((SClassArray*) obj)->s_value[i])->Get();
			qsort(tmp, ((SClassArray*) obj)->cur_size, sizeof(int), cmpfloat);
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
				((SClassFloat*) ((SClassArray*) obj)->s_value[i])->Set(tmp[i]);
			delete []tmp;
		}
	}
	return S_INVOKE_OK;
}

int SClassArray::Clear(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL)
		return S_INVOKE_INCORRECT;
	//cerr << "clear start\n";
	if (((SClassArray*) obj)->isdynamic)
	{
		((SClassArray*) obj)->d_value.clear();
		((SClassArray*) obj)->cur_size = 0;
	}
	else
	{
		for (register unsigned long i = 0; i < ((SClassArray*) obj)->cur_size; i++)
		{
			delete ((SClassArray*) obj)->s_value[i];
			((SClassArray*) obj)->s_value[i] = NULL;
		}

		((SClassArray*) obj)->cur_size = 0;
	}
	return S_INVOKE_OK;
}

int SClassArray::Join(SObject* obj, s_param* res, param_list* args)
{
	if (obj == NULL || args == NULL)
		return S_INVOKE_INCORRECT;
	param_list::iterator p = args->begin();
	if (p->type != S_STRING)
		return S_INVOKE_INCORRECT;
	std::string delim = *((std::string*) p->val);
	p++;
	if (p->type != S_OBJECT)
		return S_INVOKE_INCORRECT;
	//cerr << "ok. object.\n";
	SObject *s = (SObject*) p->val;
	if (s->GetClassName() != "String")
		return S_INVOKE_INCORRECT;
	//cerr << "ok. std::string.\n";
	std::string res_string;
	if (((SClassArray*) obj)->isdynamic)
	{
		if (((SClassArray*) obj)->ar_type == "String")
		{
			list<SObject*>::iterator p = ((SClassArray*) obj)->d_value.begin();
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->d_value.size(); i++)
			{
				res_string += ((SClassString*) *p)->Get();
				p++;
				if (p != ((SClassArray*) obj)->d_value.end())
					res_string += delim;
			}
			((SClassString*) s)->Set(res_string);
		}
		else if (((SClassArray*) obj)->ar_type == "Integer")
		{
			list<SObject*>::iterator p = ((SClassArray*) obj)->d_value.begin();
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->d_value.size(); i++)
			{
				res_string += itoa(((SClassInteger*) *p)->Get(), 10);
				p++;
				if (p != ((SClassArray*) obj)->d_value.end())
					res_string += delim;
			}
			((SClassString*) s)->Set(res_string);
		}
		else
		{
			static std::string r = "only Array of String or Integer!";
			if (res)
			{
				res->type = S_STRING;
				res->val = &r;
			}
			return S_INVOKE_INCORRECT;
		}
	}
	else
	{
		if (((SClassArray*) obj)->ar_type == "String")
		{
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
			{
				res_string
						+= ((SClassString*) ((SClassArray*) obj)->s_value[i])->Get();
				if (i + 1 == ((SClassArray*) obj)->cur_size)
					res_string += delim;
			}
			((SClassString*) s)->Set(res_string);
		}
		else if (((SClassArray*) obj)->ar_type == "Integer")
		{
			for (register unsigned long i = 0; i
					< ((SClassArray*) obj)->cur_size; i++)
			{
				res_string
						+= itoa(
								((SClassInteger*) ((SClassArray*) obj)->s_value[i])->Get(),
								10);
				if (i + 1 == ((SClassArray*) obj)->cur_size)
					res_string += delim;
			}
			((SClassString*) s)->Set(res_string);
		}
		else
		{
			static std::string r = "only Array of String or Integer!";
			if (res)
			{
				res->type = S_STRING;
				res->val = &r;
			}
			return S_INVOKE_INCORRECT;
		}
	}
	//cerr << "clear ok\n";
	return S_INVOKE_OK;
}

char* SClassArray::itoa(int val, int base)
{

	bool l = false;

	if (val < 0)
	{
		val *= -1;
		l = true;
	}

	static char buf[32] =
	{ 0 };

	for (int i = 0; i < 32; buf[i] = '\0', i++)
		;

	if (val == 0)
	{
		buf[0] = '0';
		buf[1] = '\0';
		return &buf[0];
	}

	int i = 30;

	for (; val && i; --i, val /= base)

		buf[l == true ? i + 1 : i] = "0123456789abcdef"[val % base];

	if (l)
	{
		buf[i + 1] = '-';
	}

	return &buf[i + 1];

}

int SClassArray::cmpint(const void *a, const void *b)
{
	if (*((int*) a) > *((int*) b))
		return 1;
	else if (*((int*) a) < *((int*) b))
		return -1;
	else
		return 0;
}

int SClassArray::cmpfloat(const void *a, const void *b)
{
	if (*((float*) a) > *((float*) b))
		return 1;
	else if (*((float*) a) < *((float*) b))
		return -1;
	else
		return 0;
}

int SClassArray::cmpstring(const void *a, const void *b)
{
	if (*((std::string*) a) > *((std::string*) b))
		return 1;
	else if (*((std::string*) a) < *((std::string*) b))
		return -1;
	else
		return 0;
}
